//main包中的main()函数作为程序的执行入口
package main

//导入包时会先执行该包内的常量、全局变量、init()函数,一个包可以被其他多个包import,但只会初始化一次
import (
	"encoding/json"
	"fmt"
	"gopractice/pkg1"
	"gopractice/pkg2"
	"gopractice/practice"
	"strings"

	//_ "fmt"	//如果仅仅需要导入包时执行初始化操作，并不需要使用包内的其他函数，常量等资源。则可以在导入包时使用_匿名导入.仅仅执行其init()函数
	//ff "fmt"	//起别名,可使用别名调用: ff.Println("world")
	//. "fmt"	//加点方式import,即省略前缀的包名. 可不带包名调用: fmt.Println("hello")可写成Println("world")
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode/utf8"
	"unsafe"
)

//每个包下可以有多个同名init函数,将在main之前依次执行
//对于同一个package中不同文件里面定义的init,将按照文件名字符串"从大到小"的顺序调用其内部init()
//对于不同package,如果不相互依赖的话,按照main中import的顺序调用.如果存在依赖,则最后被依赖的最先调用
func init() {
	fmt.Println("init1")
}

func init() {
	fmt.Println("init1执行完后,执行init2...")
}

/*
	//mark
	引用类型(slice、map、interface、channel)
	任何类型都实现了interface{}接口(类似java中object)

	1、数值类型（如int8、int16、uint等），默认值为0
	2、布尔类型，默认值为false
	3、字符串类型，默认值为""
	4、指针、通道、切片、字典等，默认值为nil
	5、复合类型的默认值，为所包含类型的默认值

*/

//var kaka = "1"	//定义在外面的是全局变量.必须使用var,不能使用:=

func main() {
	fmt.Println("main")

	f, err := os.Open("/test.txt")
	if err != nil {
		//报错文件不存在

		//如果一个函数或方法返回一个错误，那么按照惯例，它必须是函数返回的最后一个值
		//处理错误的惯用方法是将返回的错误与nil进行比较。nil值表示没有发生错误，而非nil值表示出现错误
		fmt.Println(err)
		//return
	} else {
		//根据f进行文件的读或写
		fmt.Println(f.Name(), "opened successfully")
	}

	fmt.Println("///////////////////////////////////////////////// init() ///////////////////////////////////////////////////////")
	fmt.Println("测试包被多次import时init()会执行几次") //init()只会被执行一次,类似static ctor()
	//重复的包只会导入一次,就像很多包都要导入fmt包一样，但它只会导入一次
	pkg1.Func1()
	pkg2.Func2()

	fmt.Println("///////////////////////////////////////////////// 变量 ///////////////////////////////////////////////////////")
	//首字母大写，代表对外部可见，首字母小写代表对外部不可见，适用于所有对象，包括函数、方法 !
	//未使用的变量将被视为错误!

	var b int //b自动初始化为0
	//++b       //错误,go中不能使用前缀递增
	b++ //正确,只能使用后缀递增

	//var x = nil             //错误
	//var y interface{} = nil //正确,还可以是 interfaces, functions, pointers, maps, slices, and channels
	o := 1          //这种简短模式只能用在函数的内部
	k, m := 2, "go" //混合变量声明

	fmt.Println(b, k, m, o)

	fmt.Println("///////////////////////////////////////////////// 语句块{} ///////////////////////////////////////////////////////")
	//Go中支持空block{}，这个大括号有自己的作用域，里面的代码只执行一次，退出大括号就退出作用域
	huhu := 1
	{
		huhu := 2
		fmt.Println(huhu) // 输出2
	}
	fmt.Println(huhu) // 输出1

	fmt.Println("///////////////////////////////////////////////// 字符串 ///////////////////////////////////////////////////////")
	//string的底层是byte数组，每个string其实只占用两个机器字长：一个指针和一个长度.string是一个底层byte数组的值类型而非指针类型

	//不带声明格式的只能在函数体中出现
	kaka := "示例字符串"
	//a, b, c := 1, 2, "str"

	//一个中文展3个字节
	println("我是中国人"[0:3]) //输出"我"

	//字母以A-Za-z的ASCII方式排列
	println("a" < "b") // true

	//len() 函数的返回值的类型为 int，表示字符串的 ASCII 字符个数或字节长度
	fmt.Println(len(kaka))
	fmt.Println(utf8.RuneCountInString(kaka))

	//遍历unicode字符
	for i := 0; i < len(kaka); i++ {
		fmt.Printf("ascii: %c  %d\n", kaka[i], kaka[i])
	}

	//遍历字符
	for idx, unicodeVal := range kaka {
		fmt.Println(idx, unicodeVal)
	}

	fmt.Println("byte截取: " + kaka[2:3])

	kakaCut := string([]rune(kaka)[:4])
	fmt.Println("中文字符串截取: " + kakaCut)

	//原始字符串，使用｀表示
	str := `start888测试\x61\142\u0042试试888end`
	fmt.Println("%s", str)

	fmt.Println(strings.Contains(str, "测试"))      //Contains
	fmt.Println(strings.ContainsAny(str, "测验考试")) //ContainsAny只要包含chars中任意一个字符即可
	fmt.Println(strings.Count(str, "8"))          //统计substr在str中出现的次数. 输出3
	fmt.Println(strings.HasPrefix(str, "start"))  //startswith
	fmt.Println(strings.HasSuffix(str, "end"))    //endwith
	fmt.Println(strings.Index(str, "试"))          //indexOf,不存在返回-1
	fmt.Println(strings.LastIndex(str, "8"))      //lastIndexOf
	fmt.Println(strings.IndexAny(str, "an"))      //查找substr中任意字符在str中出现的位置,这里的a有出现,输出2

	tmpSlice := []string{"a", "b", "c"}
	fmt.Println(strings.Join(tmpSlice, "*")) //join以特定字符拼接切片

	tmpStr := "d,e,f,g,kaka1,kaka2,kaka3"
	fmt.Println(strings.Split(tmpStr, ","))                  //split分割成切片
	fmt.Println(strings.Replace(tmpStr, "kaka", "huhu", -1)) //字符串替换,-1代表全部替换,否则指定替换个数

	fmt.Println("///////////////////////////////////////////////// const ///////////////////////////////////////////////////////")
	//go中常量只支持普通类型,不支持动态表达式
	const LENGTH int = 10            //未使用的常量不会引发编译器错误,但变量会
	const name, size = "carrot", 100 //可以一行声明多个常量
	//fmt.Println(&LENGTH)	//将报错,不能读取常量的地址

	//常量可以用作枚举,可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中，函数必须是内置函数
	const (
		aa = "abc"
		bb = len(aa)
		cc = unsafe.Sizeof(aa)
	)

	/*
		//iota，特殊常量,自增量
		const (
			a = iota   //0	iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前)，const 中每新增一行常量声明将使 iota 计数一次
			b          //1
			c          //2
			d = "ha"   //独立值，iota += 1
			e          //"ha"   iota += 1
			f = 100    //iota +=1
			g          //100  iota +=1
			h = iota   //7,恢复计数
			i          //8
		)
	*/

	//Go 没有三目运算符，所以不支持 ?: 形式的条件判断

	fmt.Println("///////////////////////////////////////////////// if ///////////////////////////////////////////////////////")
	//go的条件表达式必须是布尔类型!

	tt := 3
	if tt > 2 { //条件表达式可以省略圆括号,左侧的花括号不能另起一行!
		fmt.Println(tt)
	} else if tt == 2 { //左侧的花括号不能另起一行
	}

	//if变体.在if语句中进行初始化,分号隔开.多个初始化语句用逗号分隔
	if tmp := " "; tmp == "" {
	}

	if true {
		fmt.Print("this is if")
	} else {
		fmt.Print("this is else")
	}

	fmt.Println("///////////////////////////////////////////////// switch ///////////////////////////////////////////////////////")
	//go中的switch不需要显示的写break
	var marks int = 90

	//switch等值比较
	switch marks {
	case 90:
		fmt.Print("90")
	case 50, 60, 70:
		fmt.Print("50,60,70")
	//case ' ': //错误
	default:
		fmt.Print("...")
	}

	//switch表达式比较
	score := 5
	switch {
	case score > 1:
		fmt.Println(">1")
		fallthrough //无条件地直接跳转到下一条case并执行,即便条件不满足
	case score > 10:
		fmt.Println(">10 fallthrough")
	case score > 3:
		fmt.Println(">3")
	}

	//switch初始化表达式,注意初始化表达式末尾要加分号.如果switch没有表达式，它会匹配true
	//以下表达式声明并赋值abcx四个变量,然后判断x的值是否与a、b、c中的相等:
	switch a, b, c, x := -1, 0, 5, 5; x {
	case a, b:
		fmt.Println("(x==a)||(x==b)")
	case c:
		fmt.Println("x==c")
	}

	fmt.Println("///////////////////////////////////////////////// for ///////////////////////////////////////////////////////")
	sum := 0
	for i := 0; i <= 10; i++ { //for后的表达式也可以省略圆括号,for后的花括号也不能另起一行
		if i%2 == 0 {
			sum += 1
		} else if i > 8 {
			break
		}
	}
	fmt.Println(sum)

	//用for代替while
	for sum <= 10 {
		sum += sum
	}

	//使用for range完成数据迭代
	date := [4]string{"a", "b", "c", "d"}
	for i, str := range date {
		fmt.Println(i, str)
	}

	//死循环,相当于while(true)
	//for {
	//	fmt.Printf("这是无限循环。\n")
	//}

	fmt.Println("///////////////////////////////////////////////// 方法与函数(都是func关键字定义) ///////////////////////////////////////////////////////")
	//https://www.cnblogs.com/f-ck-need-u/p/9890624.html

	//函数:
	//函数不支持默认参数,不支持泛型.函数可以作为一个值赋值给变量
	//func quack(u User) {}

	//方法:
	//是特殊的函数,定义在某一特定的类型上(如定义在User struct上)
	//不支持方法重载.
	//可用值类型或pointer对象调用全部的方法(T或*T声明的),即类型调用自己声明的方法时不需要考虑方法集,编译器会自动转换
	//func (u User) quack() {}	//(u User)或(u *User)是该方法的接收者(receiver).有了receiver的函数才能称之为方法,receiver类型可以是 T 或 *T.但类型T不能为接口或指针类型

	//aUser := new(practice.User)
	aUser := practice.User{"a", 1, "desc"}
	aUser.Setinfo("kaka", "desc")
	fmt.Println(aUser.Getinfo()) //调用T值类型方法
	//aUser.Setinfo()              //也可以调用*T引用类型方法

	bUser := &practice.User{"b", 2, "desc2"}
	fmt.Println(bUser.Getinfo())

	//高阶函数定义:
	//1.函数可以作为另一个函数的参数(典型用法是回调函数)
	//2.函数可以返回另一个函数，即让另一个函数作为这个函数的返回值(典型用法是闭包)

	//函数传值
	flag, val := add(1, 1)
	fmt.Println(flag, val)

	//函数返回另一个函数
	addResult := added()
	fmt.Println(addResult(33, 44))

	//返回值变量的使用
	//如果不想使用某个结果返回值,可以用_接收表示丢弃
	aResult, _ := func() (a, b int) { //声明返回值变量a,b,代码中可直接使用
		a = 10
		b = 20
		return //等价于：return a,b
	}()
	fmt.Println(aResult)

	//函数的判断只能是否为nil
	fmt.Println(add == nil)

	//函数的参数是一个函数
	added2("a+b", func(a, b int) int {
		return a + b
	})

	//函数可变参数
	mutilParams("a", 1, 2, 3)

	//定义struct的方法(类似java类的方法)
	bks := Books{title: "title", Desc: "desc"}
	fmt.Println(bks.Getinfo())

	fmt.Println("///////////////////////////////////////////////// 指针 ///////////////////////////////////////////////////////")
	//x *int表示变量x是一个引用，这个引用指向的目标数据是int类型。更通用的形式是x *TYPE
	//x *int的x是一个指向int类型的引用，而&y返回的也是一个引用，所以&y的y如果是int类型的数据，&y可以赋值给x *int的x
	//*x表示x是一个引用，*x表示解除这个引用，取回x所指向的数据结构，也就是说这是 一个数据结构，只不过这个数据结构可能是内置数据类型，也可能是自定义的数据结构

	//*a是一个指针变量
	var ptr *int
	var a int = 4
	fmt.Printf("变量a的类型为 = %T\n", a)

	//&a将给出变量的实际地址
	ptr = &a
	fmt.Printf("a 的值为  %d\n", a)
	fmt.Printf("*ptr 为 %d\n", *ptr)

	year := 2020
	theptr := &year
	fmt.Printf("%T %v\n", theptr, theptr) // 打印ptr的类型和值 *int 0xc00000a0c8
	fmt.Println(*theptr)                  // 输出2020	// 通过*解除引用的形式获取year的值
	fmt.Println(year)                     // 输出2020	// 直接通过变量名获取year的值

	var x = 100
	var y = 200
	fmt.Printf("交换前，x 的值 : %d\n", x)
	fmt.Printf("交换前，y 的值 : %d\n", y)
	swap(&x, &y)
	fmt.Printf("交换后，x 的值 : %d\n", x)
	fmt.Printf("交换后，y 的值 : %d\n", y)

	fmt.Println("///////////////////////////////////////////////// 数组 ///////////////////////////////////////////////////////")
	//[]指定数目是数组,不指定则是切片
	//数组是值类型,slice切片是引用类型 !

	numbers := [4]int{1, 2, 3, 5}    //声明一个数组并初始化
	numbers[0] = 0                   //修改
	numbers2 := [...]int{2, 3, 4, 5} //通过初始化时的给定的值个数来推断数组长度.实际长度为3
	fmt.Println(numbers2[1:2])       //注意截取时"左闭右开".结果是[3]

	//数组拷贝
	var numbers3 [4]int
	numbers3 = numbers2
	fmt.Println(numbers, numbers2, numbers3)

	var n [10]int //定义长度为10的数组
	for i := 0; i < 10; i++ {
		n[i] = i + 100
	}

	//遍历数组
	for idx, val := range numbers2 {
		fmt.Println(idx, val)
	}

	//遍历map
	kvs := map[string]string{"a": "apple", "b": "banana"}
	for k, v := range kvs {
		fmt.Printf("%s -> %s\n", k, v)
	}

	fmt.Println("///////////////////////////////////////////////// slice切片 ///////////////////////////////////////////////////////")
	//todo: http//https://www.cnblogs.com/f-ck-need-u/p/9854932.html
	//Go中的slice和python不太一样,它不仅仅是一种切片动作，还是一种数据结构(就像数组一样)
	//[]指定数目是数组,不指定则是切片(长度会自动扩容,每次为原来的一倍且支持append添加元素),slice依赖于数组，它的底层就是数组
	//实际上slice是这样的结构：先创建一个有特定长度和数据类型的底层数组，然后从这个数组中选取一部分元素，返回这些元素组成的集合，并将slice指向集合中的第一个元素
	//每一个slice结构都由3部分组成：容量(capacity)、长度(length)和指向底层数组某元素的指针
	//数组是值类型,slice切片是引用类型 !

	// 声明一个nil slice
	var nilSlice []int
	fmt.Println(nilSlice)

	//长度为3，容量为5
	mySlice := make([]int, 3, 5) //先声明一个长度为5,数据类型为int的底层数组,然后从这个底层数组中从前向后取3个元素(即index从0到2)作为slice的结构
	fmt.Println(mySlice)         // 输出：[0 0 0]
	//fmt.Print(mySlice[4])		//报错,虽然容量为5,但是实际只存储了3个元素,下标越界

	mySlice2 := make([]int, 1, 2)
	mySlice = append(mySlice, mySlice2...) //使用...将mySliece2中的元素加入mySlice

	//遍历切片,长度是len()不是cap()
	for idx := 0; idx < len(mySlice2); idx++ {
	}

	//直接初始化并赋值的方式创建
	strArr := []string{"google", "duckduckgo"}
	fmt.Printf("len=%d cap=%d slice=%v\n", len(strArr), cap(strArr), strArr) //len长度;cap容量
	fmt.Println(len(strArr))

	//创建切片并append元素
	var testSlice []int
	testSlice = append(testSlice, 2, 3, 4)
	fmt.Printf("len=%d cap=%d slice=%v\n", len(testSlice), cap(testSlice), testSlice)

	//创建切片 numbers1 是之前切片的两倍容量
	numbersNew := make([]int, len(testSlice), (cap(testSlice))*2)
	//拷贝 testSlice 的内容到 numbersNew
	copy(numbersNew, testSlice)

	//已有数组直接创建切片
	myArr := [3]int{1, 2, 3}
	sliceFromArr := myArr[1:2]
	fmt.Println(sliceFromArr)        // [2]
	fmt.Printf("%T\n", sliceFromArr) //[]int

	//打印数组地址和切片地址
	fmt.Printf("%p\n", &myArr)
	fmt.Printf("%p\n", sliceFromArr)

	sliceFromArr[0] = 9
	fmt.Println(myArr) //[1 9 3]	切片的操作会影响原始数组,同时,底层数组(myArr)的修改也会影响到切片

	fmt.Println("///////////////////////////////////////////////// 结构体 ///////////////////////////////////////////////////////")

	//var bookDefault Books	//初始化,此时各字段会赋以类型默认值
	//bookDefault := new(Books)	//通过new初始化

	book := Books{title: "Go 语言", book_id: 888}
	fmt.Println(book.title)

	//或直接赋值使用
	fmt.Println(Books{"title", 1, "desc"})

	//定义匿名struct,标签用来对字段进行描述的元数据
	nonU := struct {
		name string "姓名"
		age  byte   "年纪"
	}{}
	fmt.Println(nonU.name) //未赋值,默认string为""

	//定义匿名结构体并赋值
	nonU2 := struct {
		kind string "类型"
		desc byte   "描述"
	}{"test", 1}
	fmt.Println(nonU2.kind)

	fmt.Println("///////////////////////////////////////////////// map ///////////////////////////////////////////////////////")
	//map中的元素是无序的，遍历时的顺序是随机的，即使两次以完全相同的顺序存放完全相同的元素，也无法保证遍历时的顺序
	//Go中的map是一个指针，它的底层是数组，而且用到了两个数组，其中一个更底层的数组用于打包保存key和value
	//map的值可以是任意对象，包括函数、指针、stuct等等
	//map是引用类型(指针)!

	//var myMapEmpty map[string]string	定义nilmap
	//myMapEmpty["item"] = "yvon"	//报错
	//cap(myMapEmpty)	//报错,cap不能用于map

	var emptyMap = map[int]int{} //定义一个空map
	fmt.Println(emptyMap)        //输出 map[]

	myMap := map[string]string{"name": "yvon"} //定义非空map
	myMap["gender"] = "male"
	myMap["a"] = "a"
	myMap["b"] = "b"
	myMap["c"] = "c"
	//每次遍历输出的结果顺序都不一样.要注意map是无序的!
	for k, v := range myMap {
		fmt.Println(k, v)
	}

	//interface{}表示可以接收任意类型
	var objMap = make(map[string]interface{})
	objMap["a"] = 1
	objMap["b"] = "this is string"

	var intMap = map[int]int{}
	intMap[0] = 1
	intMap[1] = 2
	intMap[2] = 3
	fmt.Println(intMap[10]) //注意:如果key存在则返回其值,如果key不存在返回类型默认值,这里输出0
	//下面方法显示判断key是否存在:
	if theVal, exists := myMap["notExist"]; exists {
		//exists为true则表示存在
		println("The value is: " + theVal)
	}

	//删除键值,不存在不会报错
	delete(myMap, "name")

	fmt.Println("///////////////////////////////////////////////// 异常 ///////////////////////////////////////////////////////")
	errResult := testError(0, 1)
	fmt.Println(errResult)

	/*
		//panic()和recover()
		//panic()用于产生错误信息并终止当前的goroutine，一般将其看作是退出panic()所在函数以及退出调用panic()所在函数的函数。例如，G()中调用F()，F()中调用panic()，则F()退出，G()也退出

		// 必须要先声明defer，否则不能捕获到panic异常
		defer func() {
			fmt.Println("c")
			if err := recover(); err != nil {
				//处理panic
				fmt.Println(err) // 这里的err其实就是panic传入的内容，55
			}
			fmt.Println("d")
		}()

		//产生panic
		panictest()
	*/

	fmt.Println("///////////////////////////////////////////////// new ///////////////////////////////////////////////////////")
	v1 := 1000
	v2 := new(float64) //new方式返回的是指针
	updateValue(&v1, v2)
	fmt.Println("v1:", v1)
	fmt.Println("v2:", *v2)

	fmt.Println("///////////////////////////////////////////////// make ///////////////////////////////////////////////////////")
	//make用于内建类型（map、slice 和channel）的内存分配.
	//make只能创建slice、map和channel,和new的区别是不返回指针,并且返回一个有初始值(非零)的T类型，而不是*T
	mk := make(chan int)           // c has type: chan int
	var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
	fmt.Println(mk, v)

	fmt.Println("///////////////////////////////////////////////// json ///////////////////////////////////////////////////////")
	//todo: 直接转换匿名结构体时返回空{}  ??
	data, errJson := json.Marshal(&struct {
		Name      string `json:"name"`
		ProductID int64  `json:"-"` // 表示不进行序列化
	}{"car", 1})
	if errJson != nil {
		fmt.Println("error:", errJson)
	} else {
		fmt.Println(string(data)) // {}
	}

	fmt.Println("///////////////////////////////////////////////// 时间和日期 ///////////////////////////////////////////////////////")
	//06年1月2日下午3点4分5秒

	//获取单位为妙的时间戳
	curSecTs := time.Now().Unix()
	//获取单位为纳秒的时间戳
	curNano := time.Now().UnixNano()
	fmt.Println(curSecTs, curNano)

	//当前时间格式化
	now := time.Now()
	fmt.Println(now.Format("2006-01-02 15:04:05"))
	fmt.Println(now.Format("2006-01-02")) //只要年份

	//获取指定日期
	now2 := time.Date(2011, 3, 11, 9, 12, 1, 0, time.Local)
	fmt.Println(now2)

	//字符串转时间
	formatTimeStr := "2017-04-11 13:33:37"
	//formatTime, errDate := time.ParseInLocation(formatTimeStr, "2006-01-02 15:04:05", time.Local)
	formatTime, errDate := time.Parse(formatTimeStr, "2006-01-02 15:04:05")
	if errDate == nil {
		fmt.Println(formatTime)
	}

	fmt.Println("///////////////////////////////////////////////// 类型转换 ///////////////////////////////////////////////////////")
	i := 32
	fmt.Println(strconv.Itoa(i)) //int转string

	ii, errCvt := strconv.Atoi("32") //string转int
	if errCvt != nil {
		fmt.Println("converted failed")
	} else {
		fmt.Println(ii)
	}

	//其他转换
	//b, err := strconv.ParseBool("true")
	//f, err := strconv.ParseFloat("3.1415", 64)
	//i, err := strconv.ParseInt("-42", 10, 64)
	//u, err := strconv.ParseUint("42", 10, 64)

	fmt.Println("///////////////////////////////////////////////// 协程(goroutines/channel) ///////////////////////////////////////////////////////")
	//Goroutine 和 channel 是 Go 语言并发编程的 两大基石。Goroutine 用于执行并发任务，channel 用于 goroutine 之间的同步、通信
	//"不要通过共享内存来通信，而要通过通信来实现内存共享"

	//本身channel就是同步的，意味着同一时间，只能有一条goroutine来操作
	//发送和接收都是阻塞的,直到另一条goroutine来读取或发送
	//通道是goroutine之间的连接，所以通道的发送和接收必须处在不同的goroutine中

	//临界资源示例
	aaa := 1
	go func() {
		aaa = 2
		fmt.Println("子goroutine。。", aaa) //2
	}()
	aaa = 3
	time.Sleep(time.Second)
	fmt.Println("main goroutine。。", aaa) //2

	messages := make(chan string)
	if messages == nil { //判断channel是否存在
		fmt.Println("通道不存在")
	}

	go func() { messages <- "ping" }()
	msg := <-messages
	fmt.Println(msg)

	fmt.Println("///////////////////////////////////////////////// 随机数 ///////////////////////////////////////////////////////")
	//1-10的随机数
	rand.Seed(time.Now().UnixNano())
	randNum := rand.Intn(10) + 1 //int
	fmt.Println(randNum)

}

func add(num1, num2 int) (bool, string) { //函数的左花括号也不能另起一行
	sumResult := num1 + num2
	return true, strconv.FormatInt(int64(sumResult), 10)
}

func swap(x *int, y *int) {
	var temp int
	temp = *x /* 保持 x 地址上的值 */
	*x = *y   /* 将 y 值赋给 x */
	*y = temp /* 将 temp 值赋给 y */
}

//定义结构体
type Books struct {
	title   string
	book_id int
	Desc    string //若首字母大写则该成员为公有成员(对外可见)，否则是私有成员(对外不可见)
}

//定义属于struct的方法. 此处类型也可以是指针(bk *Books),(bk Books)是本方法的receiver
func (bk Books) Getinfo() string {
	return bk.title + "," + bk.Desc
}

func testError(a int, b int) int {
	//手动捕获异常
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err:", err)
		}
	}()
	a = b / a
	return a
}

func updateValue(someVal *int, someVal2 *float64) {
	*someVal = *someVal + 100
	*someVal2 = *someVal2 + 1.75
}

//可变参数
func mutilParams(str string, a ...int) {
	//len(a) 	//获取参数个数
	fmt.Println("%T, %v\n", str, a)

	//可以在函数内再定义匿名函数,但不能直接定义函数!
	func(s string) {
		fmt.Println(s)
	}("hello, go!")
}

func panictest() {
	fmt.Println("a")
	panic(55)
	fmt.Println("b")
	fmt.Println("f")
}

//函数的参数是一个函数
func added2(msg string, a func(a, b int) int) {
	fmt.Println(msg, ":", a(33, 44))
}

//函数的返回值是一个函数
func added() func(a, b int) int {
	f := func(a, b int) int {
		return a + b
	}
	return f
}
